//------------------------------------------------------------------------------
// The confidential and proprietary information contained in this file may
// only be used by a person authorised under and to the extent permitted
// by a subsisting licensing agreement from ARM Limited.
//
//            (C) COPYRIGHT 2004-2017 ARM Limited.
//                ALL RIGHTS RESERVED
//
// This entire notice must be reproduced on all copies of this file
// and copies of this file may only be made by a person if such person is
// permitted to do so under the terms of a subsisting license agreement
// from ARM Limited.
//
//  Revision            : $Revision: $
//  Release information : CM3DesignStart-r0p0-00rel0
//
//------------------------------------------------------------------------------
// Purpose: DesignStart Cycle Model
//------------------------------------------------------------------------------
/*!
  \file

  This file is included inside the scope of the generated SC_MODULE to provide
  helper methods that simplify access to the ARM CycleModels C API.

  \verbatim
  For example:

    SC_MODULE(foo)
    {
      #include "carbon/carbon_scmodule.h"
      SC_CTOR(foo)
      {
        ...
      }
    };
  \endverbatim

  To use these methods, instantiate the module and call them like any
  other C++ method:

  \verbatim
    #include "libdesign.systemc.h"

    ...

    // create an instance of ARM CycleModels model foo
    foo  foo_instance("foo");

    // dump all signals to a FSDB file
    foo_instance.carbonSCWaveInitFSDB();
    foo_instance.carbonSCDumpVars();

  \endverbatim
*/


#ifdef CARBON_EXTERNAL_DOC
/*!
  The Carbon SystemC Wrapper is defined using the SC_MODULE macro,
  making it a sub-class of sc_module.
 */
class SC_MODULE
{
public:
#endif

/*!
  \brief The name of the top level module inside the ARM Cycle Models model object.
*/
const char * carbonModelName;

/*!
  \brief This member is initialized to the CarbonObjectID of the wrapped
  ARM CycleModels model, and may be used to call C API functions that do not
  have helper methods.
 */
CarbonObjectID* carbonModelHandle;

/*!
  \brief The CarbonWaveID of the waveform file being output by the
  ARM CycleModels model, or NULL if no waveforms have been configured by the
  wrapper.

  This member is used by carbonSCDumpOn, carbonSCDumpOff, and all
  other carbonSC waveform methods.

  If the SystemC Wrapper is compiled with CARBON_DUMP_VCD or
  CARBON_DUMP_FSDB defined, this will be set by the wrapper during
  initialization.

  It may also be initialized by calling carbonSCWaveInitVCD or
  carbonSCWaveInitFSDB.
 */
CarbonWaveID* carbonWaveHandle;

#ifndef CARBON_EXTERNAL_DOC
// Visual C++ 6.0 can't handle static const members, so work around using enum.
//static const size_t WAVENAME_MAX = 1000;
enum { WAVENAME_MAX = 1000 };
#endif

/*!
  \brief Initialize VCD waveform dumping.  The ARM CycleModels C API
  function carbonWaveInitVCD is called, and the result stored in
  member carbonWaveHandle.

  \param fileName The name of the VCD file to
  create.  If NULL, a file name is generated by prepending "arm_cm_"
  to the model name.

  \param timescale The units to use when recording
  values in the waveform file.  Defaults to picoseconds.
*/
CarbonWaveID* carbonSCWaveInitVCD(const char* fileName = NULL,
                                sc_time_unit timescale = SC_PS)
{
  char waveName[WAVENAME_MAX];
  if (fileName != NULL)
    strncpy(waveName, fileName, WAVENAME_MAX);
  else
    carbonGetDefaultWaveFileName(waveName);
  return carbonWaveHandle = carbonWaveInitVCD(carbonModelHandle, waveName,
                                              systemCTimeUnitToCarbonTimescale(timescale));
}

/*!
  \brief Initialize FSDB waveform dumping.  The ARM CycleModels C API
  function carbonWaveInitFSDB is called, and the result stored in
  member carbonWaveHandle.

  \param fileName The name of the FSDB file to create.  If NULL, a file name is
                  generated by prepending "arm_cm_" to the model name.
  \param timescale The units to use when recording values in the waveform file.
                   Defaults to picoseconds.
*/
CarbonWaveID* carbonSCWaveInitFSDB(const char* fileName = NULL,
                                   sc_time_unit timescale = SC_PS)
{
  char waveName[WAVENAME_MAX];
  if (fileName != NULL)
    strncpy(waveName, fileName, WAVENAME_MAX);
  else
    carbonGetDefaultWaveFileName(waveName, ".fsdb");
  return carbonWaveHandle = carbonWaveInitFSDB(carbonModelHandle, waveName,
                                               systemCTimeUnitToCarbonTimescale(timescale));
}

/*!
  \brief Initializes the wave dumper to dump in Debussy's FSDB
  format, and to automatically open a new FSDB dump file when a
  specified size limit has been reached.

  For long simulations, it may be necessary to stop writing waveform
  data to one file and open another to continue. When this function
  initiates, it opens an FSDB file \<string\>_0.fsdb. When the specified file
  limit is reached, this file will be closed and \<string\>_1.fsdb will be
  opened. This process will continue until the specified maximum number of
  files is reached, at which point the file index is returned to 0
  and \<string\>_0.fsdb is overwritten.

  This function may be called only once--multiple sets of waveform files
  for a single run is not currently supported.

  By default, the fsdb writer uses file synchronization. If you
  encounter a runtime error or warning with fsdb writing indicating
  a problem with file syncing, set the Novas environment variable,
  FSDB_ENV_SYNC_CONTROL to 'off'. For example, in csh:
  setenv FSDB_ENV_SYNC_CONTROL off

  This function cannot be used in conjunction with carbonWaveInitFSDB()
  or carbonWaveInitVCD().

  Not MT-safe.

  \param fileNamePrefix The waveforms will be dumped to files
  beginning with this string followed by _\<num\>.fsdb, where num is
  the current file index. Num begins at 0 and increases by one when an
  FSDB file reaches its megabyte limit. If the maximum number of files
  has been reached, the file index is reset to 0, and the file is
  overwritten.  If If NULL, a file name prefix is generated by
  prepending "arm_cm_" to the model name.

  \param timescale User-specified timescale. This timescale is the
  units given to the waveform dumper and can be applied to the data
  time points.

  \param limitMegs The size limit in megabytes for an FSDB file.
  Once the limit is reached, the file index is increased, and a new
  file is opened. Must be at least 2. A specification of 0 or 1 will
  automatically be upgraded to 2 and a warning will be issued.

  \param maxFiles The maximum number of FSDB files to create during the
  simulation. If 0 then always increase the file index through
  the entire simulation, never overwritting a file. (Specify 0 if you
  do not want to overwrite any FSDB file created during the simulation.)
  If 2 or greater the  maximum number of files is allowed to be written.
  In this case, the file index is reset to 0 once the maximum number of
  files has been reached causing files to be overwritten. Setting this to
  1 is not permitted.

  \retval NON-NULL A unique ID for the waveform dump.
  \retval NULL If the operation failed.
*/
CarbonWaveID* carbonSCWaveInitFSDBAutoSwitch(const char* fileNamePrefix = NULL,
                                             sc_time_unit timescale = SC_PS,
                                             unsigned int limitMegs = 1000,
                                             unsigned int maxFiles = 0)
{
  char waveNamePrefix[WAVENAME_MAX];
  if (fileNamePrefix != NULL)
    strncpy(waveNamePrefix, fileNamePrefix, WAVENAME_MAX);
  else
    carbonGetDefaultWaveFileName(waveNamePrefix, "");
  return carbonWaveHandle = carbonWaveInitFSDBAutoSwitch(carbonModelHandle,
                                                         waveNamePrefix,
                                                         systemCTimeUnitToCarbonTimescale(timescale),
                                                         limitMegs,
                                                         maxFiles);
}


/*!
  \brief Turns on waveform dumping for the current ARM Cycle Models model.

  If no wave file has been initialized, a VCD file will be
  automatically created.

  \param depth Limit the depth of the design to dump. If 0 (the
  default) dump the entire design or given scope or scopes.
  \param listOfScopesNets If non-null, or if it is equal to the
  top-level module of the ARM Cycle Models model, then the design
  hierarchy that will be dumped starts at the top of the
  design. Otherwise, this can be a list of nets or scopes which will
  be dumped. Note that scopes will be dumped in their entirety unless
  depth is non-zero.

  \sa carbonDumpVars()
*/
CarbonStatus carbonSCDumpVars(unsigned int depth = 0,
                              const char* listOfScopesNets = NULL)
{
  if (carbonWaveHandle == NULL)
    carbonWaveHandle = carbonSCWaveInitVCD();
  if (listOfScopesNets == NULL)
    listOfScopesNets = carbonModelName;
  return (carbonWaveHandle == NULL) ? eCarbon_ERROR :
    carbonDumpVars(carbonWaveHandle, depth, listOfScopesNets);
}

/*!
  \brief Dumps a waveform for a single net.

  If no wave file has been initialized, a VCD file will be
  automatically created.

  This function dumps the values of \e only the specified net.
  This function may be called multiple times prior to running the
  first schedule.

  \param handle The valid net ID for which to dump a waveform.
  \retval eCarbon_OK If the operation was successful.
  \retval eCarbon_ERROR If the specified handle is invalid.

  \sa carbonDumpVar()
*/
CarbonStatus carbonSCDumpVar(CarbonNetID* handle)
{
  if (carbonWaveHandle == NULL)
    carbonWaveHandle = carbonSCWaveInitVCD();
  return (carbonWaveHandle == NULL) ? eCarbon_ERROR :
    carbonDumpVar(carbonWaveHandle, handle);
}

/*!
  \brief Dumps waveforms for states and primary I/Os recursively.

  This function dumps the values of all states and primary
  I/Os within the specified scopes that have changed. The dump
  executes on each call of carbonSchedule(). If a scope is specified
  and the depth is non zero, all nets encountered "depth" number
  of scopes downward are dumped. This function essentially registers
  the list of states and primary I/Os to be monitored and dumped into
  a waveform file.

  \note This function will traverse the hierarchy of the design only
  if one exists, i.e., you must specify the use of the \e full design
  database when you compile the ARM Cycle Models model.

  \param depth The number of levels to traverse downward beginning
  from each specified scope. If 0, all underlying scopes are traversed.
  \param listOfScopesNets A comma separated and/or space separated list
  of scopes or nets to dump into a waveform file. At least one scope
  or net \e must be specified.
  \retval eCarbon_OK If the operation was successful.
  \retval eCarbon_ERROR If any specified scopes/nets cannot be found, or
  if the specified waveform cannot be found.
*/
CarbonStatus carbonSCDumpStateIO(unsigned int depth = 0,
                               const char* listOfScopesNets = NULL)
{
  if (carbonWaveHandle == NULL)
    carbonWaveHandle = carbonSCWaveInitVCD();
  if (listOfScopesNets == NULL)
    listOfScopesNets = carbonModelName;
  return (carbonWaveHandle == NULL) ? eCarbon_ERROR :
    carbonDumpStateIO(carbonWaveHandle, depth, listOfScopesNets);
}

/*!
  \brief Flushes the waveform file's buffer.

  \retval eCarbon_OK If the operation was successful.
  \retval eCarbon_ERROR If the specified waveform cannot be found.
*/
CarbonStatus carbonSCDumpFlush()
{
  return (carbonWaveHandle == NULL) ? eCarbon_ERROR : carbonDumpFlush(carbonWaveHandle);
}

/*!
  \brief Sets all nets to their current values, and resumes dumping.

  This function dumps \e all scopes and nets that were registered
  with carbonDumpVars() at the current time with their current values.
  This takes effect on the next carbonSchedule() call, and each
  successive call will dump any changed values.

  In order for  this function to work, the carbonSCDumpVars() function
  must already have been called.

  \retval eCarbon_OK If the operation was successful.
  \retval eCarbon_ERROR If the carbonSCDumpVars() function has not yet been
  called, or if the specified waveform cannot be found.
  \sa carbonDumpVars, carbonDumpOff
*/
CarbonStatus carbonSCDumpOn()
{
  return (carbonWaveHandle == NULL) ? eCarbon_ERROR : carbonDumpOn(carbonWaveHandle);
}

/*!
  \brief Sets all values to x and stops dumping.

  This function dumps \e all scopes and nets that were registered
  with carbonDumpVars(), at the current time with their values as 'x'.
  This takes effect on the next carbonSchedule() call. Any further calls
  to carbonDumpAll() will be ignored, and there will be no more value
  dumping on each call of carbonSchedule() until carbonDumpOn() is called.

  In order for this function to work, the carbonSCDumpVars() function must
  already have been called.

  \retval eCarbon_OK If the operation was successful.
  \retval eCarbon_ERROR If the carbonSCDumpVars() function has not yet
  been called, or if the specified waveform cannot be found.
  \sa carbonDumpVars, carbonDumpOn
*/
CarbonStatus carbonSCDumpOff()
{
  return (carbonWaveHandle == NULL) ? eCarbon_ERROR : carbonDumpOff(carbonWaveHandle);
}

/*!
  \brief Saves the state of the current simulation to a checkpoint file.

  This function saves the current state of the run to the specified
  file. All of the underlying values of the nets and the current
  time are saved. The state of waveforms is \e not saved.

  \note This function will work only if the \c -checkpoint option was specified
  when the design was compiled.

  \param filename Name of the file in which to save the state.
  \retval eCarbon_OK If the operation was successful.
  \retval eCarbon_ERROR If the operation failed.
  \sa carbonRestore, carbonStreamSave, carbonCheckpointWrite, carbonAdminAddControlCB
*/
CarbonStatus carbonSCSave(const char *filename)
{
  return carbonSave(carbonModelHandle, filename);
}

/*!
  \brief Restores a previously saved checkpoint of the simulation.

  This function restores the saved state from the specified
  checkpoint file. All the design values and the time will be
  restored; the waveform dump is \e not restored.

  \note This function will work only if the \c -checkpoint option was specified
  when the design was compiled.

  \param filename Name of the file from which to retrieve the state.
  \retval eCarbon_OK If the operation was successful.
  \retval eCarbon_ERROR If the operation failed.
  \sa carbonSave, carbonStreamRestore, carbonCheckpointRead, carbonAdminAddControlCB
*/
CarbonStatus carbonSCRestore(const char *filename)
{
  return carbonRestore(carbonModelHandle, filename);
}

#ifndef CARBON_EXTERNAL_DOC

//! This method is called by SystemC to initialize the ports.
void end_of_elaboration();

protected:
//! convert a SystemC sc_time_unit to a CarbonTimeScale
CarbonTimescale systemCTimeUnitToCarbonTimescale(sc_time_unit timescale)
{
  return CarbonTimescale((timescale  -5) * 3);
}

//! create a waveform file name
void carbonGetDefaultWaveFileName(char* waveName,
                                  const char* suffix = ".vcd")
{
  const char prefix[] = "arm_cm_";
  strcpy(waveName, prefix);

  //! replace non alpanumerical characters in moduleName with underscore
  char moduleName[WAVENAME_MAX];
  strncpy(moduleName, sc_module::name(), WAVENAME_MAX - 1);
  moduleName[WAVENAME_MAX - 1] = '\0';
  const size_t length = strlen(moduleName);
  for(size_t i = 0; i < length; i++)
    {
      unsigned char c = moduleName[i];
      if (!isalnum (c) && (c != '_'))
	{
	  moduleName[i] = '_';
	}
    }
  strncat(waveName, moduleName, WAVENAME_MAX - sizeof(prefix));
  strncat(waveName, suffix, WAVENAME_MAX - strlen(waveName) - 1);
}

static void carbonScSaveWrapperState(CarbonObjectID*, CarbonControlType, void*, const char*, int);
static void carbonScRestoreWrapperState(CarbonObjectID*, CarbonControlType, void*, const char*, int);

public:

#else
}
#endif
